package com.lyeducation.security.config;

import com.lyeducation.core.base.SystemProperties;
import com.lyeducation.security.SecurityConfig;
import com.lyeducation.services.system.SysUserServiceImpl;
import com.lyeducation.util.StringUtils;
import com.lyeducation.util.jwt.JwtUtil;
import com.lyeducation.util.jwt.JwttokenconfigEnum;
import io.jsonwebtoken.ExpiredJwtException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;

/** 认证jwtTowken 建立安全上下文（security context） */
@Slf4j
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

  @Resource(name = "sysUserServiceImpl")
  private SysUserServiceImpl sysUserServiceImpl;

  @Autowired private JwtUtil jwtUtil;

  private SessionRegistry sessionRegistry = new SessionRegistryImpl();
    @Autowired
    private SystemProperties systemProperties;
  @Override
  protected void doFilterInternal(
      HttpServletRequest request, HttpServletResponse response, FilterChain chain)
      throws ServletException, IOException {
    String url = request.getRequestURL().toString();
    log.info("requestUrl:{}", url);
    String loginUrl =
            url.substring(
                    systemProperties.getProjectAddress().length(),
                    url.length());
    //    String urlArray[] = url.split("/");
    //    String formLoginUrl = urlArray[urlArray.length - 2] + "/" + urlArray[urlArray.length - 1];
    //    String socialLoginUrl = urlArray[urlArray.length - 1];
    setHeader(response, request);
    if (StringUtils.equals(loginUrl, SecurityConstants.DEFAULT_SIGN_IN_PROCESSING_URL_FORM)
            || StringUtils.equals(loginUrl, SecurityConstants.DEFAULT_SOCIAL_PROCESS_URL)) {
      chain.doFilter(request, response);
      return;
    }
    // 当session没被禁用时能一直拿到登陆实体
    //    //    Authentication authenticationTest =
    //    // SecurityContextHolder.getContext().getAuthentication();
    //    //    if (authenticationTest != null) {
    //    //      log.info("SecurityContext.getAuthentication:" + authenticationTest.toString());
    //    //    }
    //    // 请求头为 Authorization
    //    // 请求体为 Bearer token
    String jwtToken = jwtUtil.getJwtTokenByRequestHeader(request);
    if (jwtToken != null) {
      boolean isNewToken = false;
      Map tokenMap = null;
      String jti = null;
      try {
        tokenMap = jwtUtil.validateToken(jwtToken);
      } catch (ExpiredJwtException e) {

//        尝试在可刷新token时间内获取新token
        log.info("ExpiredJwtException e:" + e);
        jti = e.getClaims().getId();
        tokenMap = e.getClaims();
        Object[] jwtTokenArray = jwtUtil.getJwtTokenArrayByRedisHash(jti);
        if (jwtTokenArray != null) {
          String redisJwtToken = jwtTokenArray[0].toString();
          if (jwtToken.equals(redisJwtToken)) {
            long refreshTokenTime = (long) jwtTokenArray[1];
            if (refreshTokenTime > System.currentTimeMillis()) {
              jwtToken = jwtUtil.generateToken(tokenMap);
              jwtUtil.addJwtTokenToRedisHash(jti, jwtToken);
              isNewToken = true;
            } else {
              jwtUtil.removeJwtTokenByRedisHash(jti);
              tokenMap = null;
            }
          } else {
            tokenMap = null;
          }
        } else {
          tokenMap = null;
        }
      } catch (Exception e) {
        //   使用 sessionRegistry设置当前登陆session过期 遍历所有SessionInformation 调用即刻过期方法为expireNow()
        //   ConcurrentSessionFilter过滤器的便会监听并执行当前的HttpSession session = request.getSession(false);
        // 如果 SessionInformation info =sessionRegistry.getSessionInformation(session.getId());不为空
        // 则再判断是否isExpired()过期如果过期则执行doLogout()踢出当前登录者，就是移除该Authentication
        // —UsernamePasswordAuthenticationToken
        //        shotOff(SecurityContextHolder.getContext());
        //        这里直接抛出异常会死循环，能被当前filter拦截下来，只有在最后的UrlAccessDecisionManager抛出的才不会
        //        throw new BadCredentialsException("未登录");
        //        如何同步redis裡面的Token過時
        //        jwtUtil.removeJwtTokenByRedisHash(jwtToken);
//        尝试获取刷新token是否未过期
        chain.doFilter(request, response);
        return;
      }
      if (tokenMap != null) {
        jti = tokenMap.get(JwttokenconfigEnum.jti.getValue()).toString();
        Object[] jwtTokenArray = jwtUtil.getJwtTokenArrayByRedisHash(jti);
        if (jwtTokenArray != null) {
          String redisJwtToken = jwtTokenArray[0].toString();
          if (redisJwtToken != null) {
            //      当前登陆者jwtToken与redis缓存的jwtTokensession不一致说明已经被再次登录当前token失效,则设置当前登陆者为匿名
            if (redisJwtToken.equals(jwtToken)) {
              //        这里还有个问题如果某次请求在刷新时间前一刻，
              // 如果在下次请求的时间过了刷新时间同时也是到了该towken的过期时间，
              // 则会让人觉得刚操作不久怎么就会话过期了
              String refreshTowken = null;
              if (!isNewToken) {
                refreshTowken = jwtUtil.refreshToken(tokenMap);
                isNewToken = true;
              }
              if (StringUtils.isNotBlank(refreshTowken)) {
                //          jwtUtil.removeJwtTokenByRedisHash(jti);
                jwtToken = refreshTowken;
              }
//              String username =
//                      StringUtils.toStringByObject(tokenMap.get(JwttokenconfigEnum.sub.getValue()));
              if (jti != null
                      && SecurityContextHolder.getContext().getAuthentication() == null) {
                UserDetails userDetails = sysUserServiceImpl.loadUserById(jti);
                //          不查数据库改为直接将towken转为UserDetails
//              UserDetails userDetails = UserDetailsImpl.createUserDetailByMap(tokenMap);
                if (userDetails != null) {
                  if (isNewToken) {
                    jwtUtil.addJwtTokenToRedisHash(jti, jwtToken);
                  }
                  response.setHeader(
                          jwtUtil.getJwttokenconfigBean().getHeader(),
                          jwtUtil.getJwttokenconfigBean().getTokenHead() + jwtToken);
                  SecurityConfig.setAuthenticationToSecurityContext(userDetails, request);
                  //            UsernamePasswordAuthenticationToken authentication =
                  //                    new UsernamePasswordAuthenticationToken(
                  //                            userDetails, userDetails.getPassword(),
                  // userDetails.getAuthorities());
                  //            authentication.setDetails(new
                  // WebAuthenticationDetailsSource().buildDetails(request));
                  //            SecurityContextHolder.getContext().setAuthentication(authentication);
                }
              }
            }
//          else {
//            new BadCredentialsException("令牌过期，异地登录");
//          }
          }
        }
      }
    }
//    new BadCredentialsException("令牌失效");
    chain.doFilter(request, response);
  }
  // 禁用session便不需要即时使当前登录者过期了
  //      /** 把当前用户踢出系统 */
  //      public void shotOff(SecurityContext context) {
  //        Authentication authentication=context.getAuthentication();
  //        if(authentication!=null) {
  //          List<SessionInformation> sessionInformations =
  //                  sessionRegistry.getAllSessions(authentication.getPrincipal(), false);
  //          for (SessionInformation sessionInformation : sessionInformations) {
  //            sessionInformation.expireNow();
  //
  //            //					.getSessionId());      //
  // sessionRegistry.removeSessionInformation(sessionInformation
  //            //      ////					.getSessionId());
  //          }
  //    }
  //  }
  private void setHeader(HttpServletResponse response, HttpServletRequest request) {
    response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
    response.setHeader("Access-Control-Allow-Credentials", "true");
    response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
    response.setHeader("Access-Control-Max-Age", "3600");
    response.setHeader("Access-Control-Allow-Headers", "Authorization");
  }
}
